﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.IO.MemoryMappedFiles;
using Go;
using GoRpc;

namespace FileService
{
    class Program
    {
        static void Main(string[] ps)
        {
            string ip = ps[0];
            int port = int.Parse(ps[1]);
            work_service work = new work_service();
            work_strand strand = new work_strand(work);
            generator.go(strand, async delegate ()
            {
                generator.children sessionChild = new generator.children();
reaccep:
                socket_tcp.acceptor accep = new socket_tcp.acceptor();
                if (!accep.bind(ip, port))
                {
                    await generator.sleep(3000);
                    goto reaccep;
                }
                while (true)
                {
                    socket_tcp socket = new socket_tcp();
                    if (!await accep.accept(socket))
                    {
                        return;
                    }
                    Rpc.Methods methods = new Rpc.Methods();
                    Rpc.Session session = new Rpc.Session(strand, socket, methods);
                    methods.Bind("GetDrives", Rpc.Methods.Cast(async delegate ()
                    {
                        return await generator.send_task(delegate ()
                        {
                            return Array.ConvertAll(DriveInfo.GetDrives(), (DriveInfo drive) => drive.Name);
                        });
                    }));
                    methods.Bind("GetFiles", Rpc.Methods.Cast(async delegate (string path)
                    {
                        return await generator.send_task(delegate ()
                        {
                            return Array.ConvertAll(Directory.GetFiles(path), (string v) => Path.GetFileName(v));
                        });
                    }));
                    methods.Bind("GetDirectories", Rpc.Methods.Cast(async delegate (string path)
                    {
                        return await generator.send_task(delegate ()
                        {
                            return Array.ConvertAll(Directory.GetDirectories(path), (string v) => Path.GetFileName(v));
                        });
                    }));
                    methods.Bind("FileSize", Rpc.Methods.Cast(async delegate (string path)
                    {
                        return await generator.send_task(delegate ()
                        {
                            return new FileInfo(path).Length;
                        });
                    }));
                    methods.Bind("DirectoryInfo", Rpc.Methods.Cast(async delegate (string path)
                    {
                        return await generator.send_task(delegate ()
                        {
                            return new DateTime[]
                            {
                                Directory.GetCreationTime(path),
                                Directory.GetLastWriteTime(path),
                                Directory.GetLastAccessTime(path),
                            };
                        });
                    }));
                    methods.Bind("ReadFile", Rpc.Methods.Cast(async delegate (string path, string writeFile)
                    {
                        generator self = generator.self;
                        Rpc.Session.CancelWait().AppendNotify(() => self.stop());
                        FileStream file = null;
                        DateTime[] info = null;
                        try
                        {
                            generator.lock_stop();
                            await generator.send_task(delegate ()
                            {
                                try
                                {
                                    file = File.OpenRead(path);
                                    info = new DateTime[]
                                    {
                                        File.GetCreationTime(path),
                                        File.GetLastWriteTime(path),
                                        File.GetLastAccessTime(path),
                                    };
                                }
                                catch (Exception) { }
                            });
                            generator.unlock_stop();
                            if (null == file)
                            {
                                return false;
                            }
                            Console.WriteLine(($"Begin Send {path}"));
                            if (file.Length > 0)
                            {
                                Rpc.SendDoubleBuffer dbuff = new Rpc.SendDoubleBuffer(1024 * 1024);
                                dbuff.Async(() => file.Read(dbuff.ReadBuff, 0, (int)Math.Min(dbuff.ReadBuff.Length, file.Length)));
                                Rpc.Session.Self.OverSend(file.Length, async delegate (long offset, long count)
                                {
                                    await dbuff.Exchange();
                                    if (dbuff.Error)
                                    {
                                        return;
                                    }
                                    var data = dbuff.SendData;
                                    if (count - data.Count > 0)
                                    {
                                        dbuff.Async(() => file.Read(dbuff.ReadBuff, 0, (int)Math.Min(dbuff.ReadBuff.Length, count - data.Count)));
                                    }
                                    await session.Send(data);
                                });
                            }
                            var res = await session.Call(writeFile, info);
                            if (res.state != Rpc.ReplyState.Success)
                            {
                                Console.WriteLine(($"Fail Send {path}"));
                                return false;
                            }
                            Console.WriteLine(($"End Send {path}"));
                            return true;
                        }
                        catch (generator.stop_exception)
                        {
                            Console.WriteLine(($"Cancel Send {path}"));
                            throw;
                        }
                        finally
                        {
                            file?.Dispose();
                        }
                    }));
                    methods.Bind("CreateDirectory", Rpc.Methods.Cast(async delegate (string path)
                    {
                        Console.WriteLine(($"CreateDirectory {path}"));
                        await generator.send_task(delegate ()
                        {
                            Directory.CreateDirectory(path);
                        });
                    }));
                    methods.Bind("CreateFile", Rpc.Methods.Cast(async delegate (string path)
                    {
                        Console.WriteLine(($"CreateFile {path}"));
                        await generator.send_task(delegate ()
                        {
                            File.Create(path).Dispose();
                        });
                    }));
                    methods.Bind("WriteFile", Rpc.Methods.Cast(async delegate (string path, DateTime[] fileInfo)
                    {
                        if (File.Exists(path))
                        {
                            Console.WriteLine(($"Exists {path}"));
                            return false;
                        }
                        Rpc.OverRecv[] recvs = Rpc.Session.OverRecv();
                        FileStream file = null;
                        bool completed = false;
                        try
                        {
                            Console.WriteLine(($"Begin Recv {path}"));
                            generator.lock_stop();
                            await generator.send_task(delegate ()
                            {
                                try
                                {
                                    file = File.Open(path, FileMode.Create);
                                }
                                catch (Exception) { }
                            });
                            generator.unlock_stop();
                            if (null == file)
                            {
                                return false;
                            }
                            if (null != recvs)
                            {
                                Rpc.RecvDoubleBuffer dbuff = new Rpc.RecvDoubleBuffer(1024 * 1024);
                                long offset = 0;
                                for (offset = 0; offset < recvs[0].Length;)
                                {
                                    int len = await Rpc.Session.Self.Recv(recvs[0], dbuff.RecvBuff, 0, dbuff.RecvBuff.Length);
                                    if (len <= 0)
                                    {
                                        break;
                                    }
                                    await dbuff.Exchange();
                                    if (dbuff.Error)
                                    {
                                        break;
                                    }
                                    dbuff.Async(() => file.Write(dbuff.WriteData, 0, len));
                                    offset += len;
                                }
                                await dbuff.Exchange();
                                if (!dbuff.Error)
                                {
                                    completed = offset == recvs[0].Length;
                                }
                                if (recvs[0].Cancel)
                                {
                                    Console.WriteLine(($"Cancel Recv {path}"));
                                }
                                else
                                {
                                    Console.WriteLine(($"End Recv {path}"));
                                }
                                return !recvs[0].Cancel;
                            }
                            else
                            {
                                completed = true;
                                return true;
                            }
                        }
                        finally
                        {
                            generator.lock_stop();
                            await generator.send_task(delegate ()
                            {
                                if (null != file)
                                {
                                    try
                                    {
                                        file.Dispose();
                                        if (completed)
                                        {
                                            File.SetCreationTime(path, fileInfo[0]);
                                            File.SetLastWriteTime(path, fileInfo[1]);
                                            File.SetLastAccessTime(path, fileInfo[2]);
                                        }
                                        else
                                        {
                                            File.Delete(path);
                                        }
                                    }
                                    catch (Exception) { }
                                }
                            });
                            generator.unlock_stop();
                        }
                    }));
                    sessionChild.free_go(async delegate ()
                    {
                        Console.WriteLine("new session");
                        session.Start();
                        await session.Wait();
                        Console.WriteLine($"free session {session.Code}");
                    });
                }
            });
            work.run();
        }
    }
}
